package is.client.ui;

import is.client.Locale;
import is.client.controller.command.*;
import is.common.exception.ImpossibleException;
import is.common.exception.NumberFormatInsteadRuntimeException;
import is.common.interfaces.mvc.UserInterface;
import is.common.util.Regex;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Comparator;
import java.util.LinkedList;

/**
 * @author aspr0
 *         Date: 28.03.12
 *         Time: 3:21
 */
public class Gui implements UserInterface
{
    private JFrame frame;
    private JPanel rootPane;
    private JTextField serverTextField;
    private JButton connectButton;
    private JPanel connectionOperationsPane;
    private JLabel serverPortLabel;
    private JTabbedPane tabbedPane;
    private JPanel employeesTab;
    private JPanel departmentTab;
    private JButton findButton;
    private JButton addButton;
    private JButton clearFieldsButton;
    private JButton dropSelectedButton;
    private JTable table;
    private JPanel buttonBar;
    private JPanel dataOperationsPane;
    private JScrollPane tableScrollPane;
    private JTextField etDepartmentTextField;
    private JTextField etMiddleNameTextField;
    private JTextField etFirstNameTextField;
    private JTextField etTelephoneTextField;
    private JTextField etLastNameTextField;
    private JTextField etSalaryTextField;
    private JTextField dtNameTextField;
    private JTextField dtBossLastNameTextField;
    private JLabel dataOperationsInstructLabel;
    private JTextField dtBossFirstNameTextField;
    private JTextField dtBossMiddleNameTextField;
    private JLabel statusLabel;
    private JLabel status;
    private JButton reloadButton;
    private JTextField etId;
    private JTextField dtId;
    private JTextField controlPortTextField;
    private JTextField portTextField;
    private JLabel controlPortLabel;
    private JLabel portLabel;

    private DefaultTableModel tableModel;
    private int currentSectionNum;

    private Icon connectIcon;
    private Icon disconnectIcon;
    private ActionListener connectListener;
    private ActionListener disconnectListener;
    private TableModelListener tableModelListener;


    public Gui()
    {
        connectListener = new ActionListener()
        {
            public void actionPerformed(ActionEvent actionEvent)
            {
                new ConnectCommand(serverTextField.getText(), portTextField.getText(), controlPortTextField.getText())
                        .execute();
                new ListCommand(currentSectionNum).execute();
            }
        };

        disconnectListener = new ActionListener()
        {
            public void actionPerformed(ActionEvent actionEvent)
            {
                new DisconnectCommand().execute();
            }
        };

        WindowAdapter closeOperationListener = new WindowAdapter()
        {
            public void windowClosing(WindowEvent evt)
            {
                new QuitCommand().execute();
            }
        };

        ActionListener findButtonListener = new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent actionEvent)
            {
                new FindCommand(currentSectionNum, collectFieldValues()).execute();
            }
        };

        ChangeListener tabbedPaneSwitchListener = new ChangeListener()
        {
            @Override
            public void stateChanged(ChangeEvent evt)
            {
                currentSectionNum = tabbedPane.getSelectedIndex();
                new ListCommand(currentSectionNum).execute();
            }
        };

        ActionListener reloadButtonListener = new ActionListener()
        {

            @Override
            public void actionPerformed(ActionEvent actionEvent)
            {
                new ListCommand(currentSectionNum).execute();
            }
        };

        ActionListener clearButtonListener = new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent actionEvent)
            {
                clearFieldValues();
            }
        };

        ActionListener addButtonListener = new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent actionEvent)
            {
                String[] row = collectFieldValuesNoId();

                try
                {
                    telephoneSalaryCorrect(row);
                }
                catch (NumberFormatInsteadRuntimeException e)
                {
                    exposeWarnMsg("wrong format salary tel");//todo
                    return;
                }

                new AddCommand(currentSectionNum, row).execute();
                new ListCommand(currentSectionNum).execute();
            }
        };

        ActionListener dropSelectedButtonListener = new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent actionEvent)
            {
                String[] selectedRowIds = getSelectedRowIds();

                new DropCommand(currentSectionNum, selectedRowIds).execute();
                new ListCommand(currentSectionNum).execute();
            }
        };


        tableModelListener = new TableModelListener()
        {
            @Override
            public void tableChanged(TableModelEvent tableModelEvent)
            {
                int changedRowNum = tableModelEvent.getFirstRow();
                int columnCount = tableModel.getColumnCount();
                String[] row = new String[columnCount];

                for (int columnNum = 0; columnNum < columnCount; columnNum++)
                {
                    row[columnNum] = (String) tableModel.getValueAt(changedRowNum, columnNum);
                }

                new EditCommand(currentSectionNum, row).execute();
                new ListCommand(currentSectionNum).execute();
            }
        };


        connectIcon = new ImageIcon(getClass().getResource("/is/client/ui/icon/network-transmit-receive.png"));
        disconnectIcon = new ImageIcon(getClass().getResource("/is/client/ui/icon/network-offline.png"));

        frame = new JFrame("Information system v 2.0");
        tableModel = new DefaultTableModel();


        /* Setting close operation to nothing cause we got a closeOperationListener */
        frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);

        /* adding listeners */
        frame.addWindowListener(closeOperationListener);

        connectButton.addActionListener(connectListener);
        findButton.addActionListener(findButtonListener);
        tabbedPane.addChangeListener(tabbedPaneSwitchListener);
        reloadButton.addActionListener(reloadButtonListener);
        clearFieldsButton.addActionListener(clearButtonListener);
        tableModel.addTableModelListener(tableModelListener);
        addButton.addActionListener(addButtonListener);
        dropSelectedButton.addActionListener(dropSelectedButtonListener);

        table.setModel(tableModel);

        TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel)
        {
            @Override
            public Comparator getComparator(int colNum)
            {
                return new TableComparator(currentSectionNum, colNum);
            }
        };

        table.setRowSorter(sorter);

        /* installing rootPane into frame */
        frame.setContentPane(this.rootPane);
        frame.pack();

        /* centering window, affects correct only if called after pack() */
        frame.setLocationRelativeTo(null);
    }

    public void activate()
    {
        frame.setVisible(true);

    }

    public void deactivate()
    {
        frame.setVisible(false);
    }

    @Override
    public void exposeInfoMsg(String msg, Object... subs)
    {
        popupMsg(1, "Information", msg, subs);
    }

    @Override

    public void exposeWarnMsg(String msg, Object... subs)
    {
        popupMsg(2, "Warning", msg, subs);
    }

    @Override

    public void exposeErrMsg(String msg, Object... subs)
    {
        popupMsg(0, "Error", msg, subs);
    }

    @Override
    public void exposeRows(int sectionNum, String[] header, String[][] rows)
    {
        /* for listener not to fire events while reloading content */
        tableModel.removeTableModelListener(tableModelListener);

        while (tableModel.getRowCount() > 0)
        {
            tableModel.removeRow(0);
        }

        tableModel.setColumnIdentifiers(header);

        for (String[] row : rows)
        {
            tableModel.addRow(row);
        }

        /* listening again */
        tableModel.addTableModelListener(tableModelListener);
    }

    @Override
    public void exposeConnectionEstablished()
    {
        connectButton.setIcon(disconnectIcon);
        connectButton.removeActionListener(connectListener);
        connectButton.addActionListener(disconnectListener);
        status.setText(Locale.GUI_STATUS_ESTABLISHED);
        connectButton.setText(Locale.GUI_CONNECT_BUTTON_ESTABLISHED);

        table.setEnabled(true);
        reloadButton.setEnabled(true);
        findButton.setEnabled(true);
        addButton.setEnabled(true);
        dropSelectedButton.setEnabled(true);
    }

    @Override
    public void exposeConnectionBroken()
    {
        connectButton.setIcon(connectIcon);
        connectButton.removeActionListener(disconnectListener);
        connectButton.addActionListener(connectListener);
        status.setText(Locale.GUI_STATUS_BROKEN);
        connectButton.setText(Locale.GUI_CONNECT_BUTTON_BROKEN);

        table.setEnabled(false);
        reloadButton.setEnabled(false);
        findButton.setEnabled(false);
        addButton.setEnabled(false);
        dropSelectedButton.setEnabled(false);
    }

    /**
     * @param type  0 for error, 1 information, 2 warning
     * @param title
     * @param msg
     * @param subs
     */
    private void popupMsg(int type, String title, String msg, Object... subs)
    {
        msg = String.format(msg, subs);
        JOptionPane.showMessageDialog(rootPane, msg, title, type);
    }

    private String[] collectFieldValues()
    {
        String[] values;
        int i;

        switch (currentSectionNum)
        {
            case 0:
                values = new String[7];
                i = 0;
                values[i++] = etId.getText();
                values[i++] = etLastNameTextField.getText();
                values[i++] = etFirstNameTextField.getText();
                values[i++] = etMiddleNameTextField.getText();
                values[i++] = etDepartmentTextField.getText();
                values[i++] = etTelephoneTextField.getText();
                values[i] = etSalaryTextField.getText();
                break;

            case 1:
                values = new String[5];
                i = 0;
                values[i++] = dtId.getText();
                values[i++] = dtNameTextField.getText();
                values[i++] = dtBossLastNameTextField.getText();
                values[i++] = dtBossFirstNameTextField.getText();
                values[i] = dtBossMiddleNameTextField.getText();
                break;

            default:
                throw new ImpossibleException("currentSectionNum is not 0 or 1");
        }

        return values;
    }

    private String[] collectFieldValuesNoId()
    {
        String[] values;
        int i;

        switch (currentSectionNum)
        {
            case 0:
                values = new String[6];
                i = 0;
                values[i++] = etLastNameTextField.getText();
                values[i++] = etFirstNameTextField.getText();
                values[i++] = etMiddleNameTextField.getText();
                values[i++] = etDepartmentTextField.getText();
                values[i++] = etTelephoneTextField.getText();
                values[i] = etSalaryTextField.getText();
                break;

            case 1:
                values = new String[4];
                i = 0;
                values[i++] = dtNameTextField.getText();
                values[i++] = dtBossLastNameTextField.getText();
                values[i++] = dtBossFirstNameTextField.getText();
                values[i] = dtBossMiddleNameTextField.getText();
                break;

            default:
                throw new ImpossibleException("currentSectionNum is not 0 or 1");
        }

        return values;
    }

    private void clearFieldValues()
    {
        String setTo = "";

        switch (currentSectionNum)
        {
            case 0:
                etId.setText(setTo);
                etLastNameTextField.setText(setTo);
                etFirstNameTextField.setText(setTo);
                etMiddleNameTextField.setText(setTo);
                etDepartmentTextField.setText(setTo);
                etTelephoneTextField.setText(setTo);
                etSalaryTextField.setText(setTo);
                break;

            case 1:
                dtId.setText(setTo);
                dtNameTextField.setText(setTo);
                dtBossLastNameTextField.setText(setTo);
                dtBossFirstNameTextField.setText(setTo);
                dtBossMiddleNameTextField.setText(setTo);
                break;
        }
    }

    private void telephoneSalaryCorrect(String[] row) throws NumberFormatInsteadRuntimeException
    {
        if (currentSectionNum == 1)
        {
            return;
        }

        String telephone = row[4];
        String salary = row[5];
        String spacesAndNumbersMask = "\\d+\\s*";

        if (Regex.stringMatches(telephone, spacesAndNumbersMask))
        {
            if (Regex.stringMatches(salary, spacesAndNumbersMask))
            {
                return;
            }
        }
        throw new NumberFormatInsteadRuntimeException();
    }

    private String[] getSelectedRowIds()
    {
        int[] selectedRowNums = table.getSelectedRows();
        LinkedList<String> selectedRowIdsList = new LinkedList<String>();

        for (int selectedRowNum : selectedRowNums)
        {
            String selectedRowId = (String) tableModel.getValueAt(selectedRowNum, 0);
            selectedRowIdsList.add(selectedRowId);
        }

        return selectedRowIdsList.toArray(new String[]{});
    }
}
